home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Leisure Game Pak
/
Leisure Game Pak.iso
/
lpgame1
/
04
/
source
/
drawstr.pas
next >
Wrap
Pascal/Delphi Source File
|
1994-08-17
|
12KB
|
343 lines
UNIT DRAWSTR;
(* This UNIT supplies enhanced line-drawing-routines
(all lines in angles of k * 45°, with k ε [0..7] in 2 colors).
The drawing's shape is stored in STRINGs.
L o O
The commands are: [k]{^}DIR with DIR in l * r draw (or move) k
U u R
steps in DIRection using the actual color
(if k is omitted, draw/move 1 step)
^ if uppercase(actual_letter) then INC(k)
. actual color is COLOR1
: " " is COLOR2
- switch to MOVE mode (no drawing is done)
+ " to DRAW mode
*)
INTERFACE
CONST LEFT_ALIGNED_TEXT = 0;
CENTERED_TEXT = 1;
RIGHT_ALIGNED_TEXT = 2;
(* SetDrawColSizeAlign sets the colors and the step *)
PROCEDURE SetDrawColSize(col1, col2, step : WORD);
{ the DTextWidth function returns the width of TXT when drawn with
the current settings }
FUNCTION DTextWidth(txt : STRING) : INTEGER;
{ The DrawText-routines draw a textstring }
PROCEDURE DrawText(txt : STRING);
PROCEDURE DrawTextAt(x, y : WORD; txt : STRING; alignment : BYTE);
{ DrawString draws the shape stored in s, is_large = TRUE => draw UPCASE letter }
PROCEDURE DrawStringAt(x, y : WORD; s : STRING; is_large : BOOLEAN);
IMPLEMENTATION
USES GRAPH;
TYPE DIR_TYPE = 0..7;
STEP_TYPE = ARRAY[DIR_TYPE] OF INTEGER;
(* IMPLEMENTATION L o O
The eight directions l * r shall be coded into an ARRAY OF INTEGER
U u R
using a hash function. All we have to do is find a simple hash function
that associates the character (e.g. 'r') with the corresponding
direction ('r' should be (+1, 0)).
To find this function we take advantage of the ASCII coding for
characters. In ASCII a character is stored in 8 bits, say: 76543210.
Watching closely we see that bits 5,4,0 are enough to distinguish the
eight characters we want to use.
The coding is as follows 540
L = 000 = 0, l = 100 = 4
O = 001 = 1, o = 101 = 5
R = 010 = 2, r = 110 = 6
U = 011 = 3, u = 111 = 7
Doing this we gain a (comparably) fast access to the directions by
letter.
Alternatives:
Accessing the directions could also be done in a CASE statement
(which would be more portable), or by an ARRAY['L'..'u'] OF INTEGER
(which would waste too much memory), or if you consider taking 0..7
as directions and letters ('a'..'z') as distances (1..26) ...
In fact, this last alternative is the easiest to design, since we need
no hash function then (as the directions fit in a 0..7 ARRAY already),
yet this would be much harder to use later on
*)
{ L, O, R, U, l, o, r, u }
CONST SINGLESTEP_X : STEP_TYPE = (-1, 1, 1,-1,-1, 0, 1, 0);
SINGLESTEP_Y : STEP_TYPE = (-1,-1, 1, 1, 0,-1, 0, 1);
(* default settings for the colors : *)
COLOR1 : WORD = 15; (* WHITE *)
COLOR2 : WORD = 8; (* DKGREY *)
CONVERT : ARRAY[' '..'Z'] OF CHAR =
('<','>','/','<','<','<','<','/','<','<','<','<',';','=','.','<',
'0','1','2','3','4','5','6','7','8','9',':',';','<','<','<','?',
'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z');
SHAPE : ARRAY['.'..'Z'] OF STRING[51] =
{.} ('-r.3o4r:3u4l-5r',
{' = /} '-10o.3o4r:3u2U2l.2O:2l-5R5u',
{0} '-r.L9oO7r:R9uU7l-3O:o2O.3u2l-3o:3o2r.u2U-6R',
{1} '-2r.8o:2l.3o7r:11u5l-6r',
{2} '-r.L5oO4r2o:4l.2o7r:R4uU4l2u.5r:3u8l-9r',
{3} '.3o5r2o:4l.2o4r2o:4l.2o6r:R3uR5uU8l-10r',
{4} '-5r.4o:5l.7o4r:4u.2r4o3r:4u.r:3ul4u4l-6r',
{5} '.3o5r2o:5l.6o8r:2u4l2u.5r:R5uU8l-10r',
{6} '-r.L9oO7r:2u4l2u.4r:R5uU7l-3O:2o2r.2u2l-3R3r',
{7} '-2r.5o3O:5l.3o9r:4u2U5u5l-8r',
{8} '-r.L5oO3oO6r:R3uR5uU8l-3O:2o3r.2u3l-4o:2o3r.2u3l-7R',
{9} '.3o5r2o:4l.L4oO7r:R9uU8l-3O4o:2o2r.2u2l-7R',
{:} '-Oo.3o4r:3u4l-4o.3o4r:3u4l-6R',
{, = ;} '-r.3o4r:3u2U2l.2O:2l-5r',
{ = <} '-8r',
{- = =} '-O4o.3o8r:3u8l-5R5r',
{! = >} '-r.3o4r:3u4l-4o.8o4r:8u4l-4Rr',
{?} '-2r.3o4r:3u4l-4o.3oO3r2o:5l.2o7r:R4uU2l2u4l-4R5r',
{@} '-2O.2L4o2O4r:2R4u2U4l-o.5O:4uU4l-L:5O.4lU4u-4R4r',
{A} '.10^oO8r:R10^u4l.5o-2L.2r2o:2l2u-2R:2l5u4l-11r',
{B} '.11^o8r:R3^uR5uU9l-3Or.3r2o:3l2u-4o.2r2o:2l2u-7R',
{C} '-r.L9^oO7r:2^u4l6u.5r:3u8l-9r',
{D} '.11^o9r:R9^uU9l-3Or.2r6o:2l6u-3R4r',
{E} '.11^o8r:2^u4l2u.3r:2u3l2u.5r:3u9l-10r',
{F} '.11^o8r:2^u4l2u.3r:2u3l5u4l-9r',
{G} '-r.L9^oO7r:2^u4l6u.2r3o3r:6u8l-9r',
{H} '.11^o4r:4u.2r4o4r:11^u4l.5o:2l5u4l-11r',
{I} '.11^o5r:11^u5l-6r',
{J} '.4o2rO6^o5r:9^u2U6l-9r',
{K} '.11^o4r:3^u.rO2^o4r:3^u2U2R4u4l.3oL:l4u4l-11r',
{L} '.11^o5r:8u.4r:3^u9l-10r',
{M} '.11^o4^r:2R.2O4^r:11^u4^l.5^o:2U.2L:5^u4^l-13^^r',
{N} '.11^o4r:3R.3o4r:11^u4l.3o3L:6u4l-12r',
{O} '-r.L9^oO8r:R9^uU8l-3O:6o2r.6u2l-3R4r',
{P} '.11^o9r:R4^uU5l5u4l-4O3o:2^o3r.2^u3l-7R',
{Q} '-r.L9^oO8r:R9^uU8l-3O:6o2r.4ul2ul-3R4r',
{R} '.11^o9r:R3^uUR5u4l.5o:2l5u4l-4O3o:2^o3r.^uU2l-7R',
{S} '.3o5r2o:4l.L4^oO7r:2^u4l2u.4r:R5uU8l-10r',
{T} '-2r.9o:2l.2^o9r:2^u2l9u5l-8r',
{U} '-2r.2L9^o4r:8u.2r8o4r:9^u2U6l-9r',
{V} '-4r.4L7^o4r:6uR.O6o4r:7^u4U2l-7r',
{W} '.11^o5r:5^u.2O:2R.5^o5r:11^u5l.2L:2U5l-15r',
{X} '.4o2O2L3^o4r:2^uR.O2^o4r:3^u2U2R4u4l.3oL:U3u4l-11r',
{Y} '-3r.5o3L3^o4r:2^uR.O2^o4r:3^u3U5u4l-8r',
{Z} '.4o5O:5l.2^o8^r:4u3^U.4r:4u9l-10r');
TotalWidth : LONGINT = 0;
VAR STEP_X, STEP_Y : STEP_TYPE;
{
TYPE FILL_STATE_TYPE = (INSIDE, OUTSIDE, FROM_INSIDE, FROM_OUTSIDE);
The ColorFill-procedure is in comments as it doesn't work correctly ...
PROCEDURE ColorFill(x0, y0, width, height,
firstcol, lastcol,
BckgrndCol : WORD);
CONST LONGLEN = 2; (* the maximum height of a border *)
VAR state : FILL_STATE_TYPE;
color, len,
col_height,
x, y : WORD;
color_dir : SHORTINT;
lastline : ARRAY [0..15] OF WORD;
BEGIN
IF firstcol > lastcol then
color_dir := -1
ELSE
color_dir := 1;
col_height := height DIV SUCC(lastcol - firstcol) * color_dir;
lastline[firstcol] := y0 + col_height;
color := firstcol;
REPEAT
lastline[color + color_dir] := lastline[color] + SUCC(col_height);
INC(color, color_dir);
UNTIL (color = lastcol);
FOR x := x0 TO x0 + PRED(width) DO
BEGIN
state := OUTSIDE;
(* slightly shift the colors *)
color := firstcol;
WHILE (color <> lastcol) DO
BEGIN
INC(lastline[color]);
IF (lastline[color] > 1) THEN
DEC(lastline[color], Random(3));
INC(color, color_dir);
END; (* WHILE color *)
color := firstcol;
FOR y := y0 TO y0 + PRED(height) DO
BEGIN
IF (y > lastline[color]) THEN
INC(color, color_dir);
IF (GetPixel(x, y) = BckgrndCol) THEN
BEGIN
CASE state OF
FROM_INSIDE : IF (len > LONGLEN) AND
(GetPixel(PRED(x), y) <> BckgrndCol) THEN
state := INSIDE
ELSE
state := OUTSIDE;
FROM_OUTSIDE : IF (len > LONGLEN) AND
(GetPixel(PRED(x), y) = BckgrndCol) THEN
state := OUTSIDE
ELSE
state := INSIDE;
END; (* CASE *)
len := 0;
END (* IF *)
ELSE
CASE state OF
FROM_INSIDE, FROM_OUTSIDE : INC(len);
INSIDE : state := FROM_INSIDE;
OUTSIDE : state := FROM_OUTSIDE;
END; (* CASE *)
IF (state = INSIDE) THEN
PutPixel(x, y, color);
END; (* FOR y *)
END; (* FOR x *)
END; (* ColorFill *)
}
(* SetDrawColSizeAlign sets the colors and the step *)
PROCEDURE SetDrawColSize(col1, col2, step : WORD);
VAR dir : BYTE;
BEGIN
COLOR1 := col1; COLOR2 := col2;
FOR dir := 0 TO 7 DO
BEGIN
STEP_X[dir] := SINGLESTEP_X[dir] * step;
STEP_Y[dir] := SINGLESTEP_Y[dir] * step;
END; (* FOR *)
END; (* SetDrawColSize *)
(* DrawString draws the shape stored in s,
is_large = TRUE => draw UPCASE letters
really_draw = TRUE => really draw the shape
= FALSE => just calculate the the string's total width *)
PROCEDURE DrawString(s : STRING; is_large, really_draw : BOOLEAN);
VAR c : CHAR;
t : BYTE; (* max string-length is 255 *)
len : WORD;
dir : DIR_TYPE; (* dir ε [0..7] *)
draw_is_on : BOOLEAN;
sx, sy : INTEGER; (* step in x,y direction *)
BEGIN
t := 0;
draw_is_on := TRUE;
len := 0; { length of the next line }
WHILE (t < Length(s)) DO
BEGIN
INC(t); c := s[t]; (* get next char *)
CASE c OF
'L'..'W',
'l'..'w' : BEGIN
(* calculate hash function:
bits 5,4 -SHIFT-> bits 2,1 *)
dir := ((ORD(c) AND $30) SHR 3) OR (ORD(c) AND 1);
sx := STEP_X[dir];
sy := STEP_Y[dir];
{note that len=0 and len=1 are equivalent}
IF (len > 0) THEN
BEGIN
sx := len * sx; sy := len * sy;
len := 0; (* reset len *)
END;
IF (really_draw) THEN
IF (draw_is_on) THEN
LineRel(sx, sy)
ELSE
MoveRel(sx, sy)
ELSE
INC(TotalWidth, sx);
END; (* L..u *)
'0'..'9': len := len * 10 + ORD(c) - ORD('0');
'^' : IF (is_large) THEN {enlarge this line}
IF (len < 2) THEN len := 2 {0, 1 => 2}
ELSE INC(len);
'+','-' : draw_is_on := (c = '+');
'.' : BEGIN
draw_is_on := TRUE;
SetColor(COLOR1);
END; (* . *)
':' : BEGIN
draw_is_on := TRUE;
SetColor(COLOR2);
END; (* : *)
END; (* CASE *)
END; (* WHILE *)
END; (* DrawString *)
PROCEDURE DrawStringAt(x, y : WORD; s : STRING; is_large : BOOLEAN);
BEGIN
MoveTo(x, y);
(* now: draw (not measure the string) *)
DrawString(s, is_large, TRUE);
END; { DrawStringAt }
{ the DTextWidth function returns the width of TXT when drawn with
the current settings }
FUNCTION DTextWidth(txt : STRING) : INTEGER;
VAR p : BYTE; {max. string length is 255}
BEGIN
TotalWidth := 0;
FOR p := 1 TO LENGTH(txt) DO
{ for uppercase letters 2nd argument is TRUE else FALSE }
DrawString(SHAPE[CONVERT[UPCASE(txt[p])]], UPCASE(txt[p]) = txt[p], FALSE);
DTextWidth := TotalWidth;
END; (* DTextWidth *)
{ The DrawText-routines draw a textstring }
PROCEDURE DrawText(txt : STRING);
VAR p : BYTE; {max. string length is 255}
total_wid : INTEGER;
BEGIN
FOR p := 1 TO LENGTH(txt) DO
{ for uppercase letters 2nd argument is TRUE else FALSE }
DrawString(SHAPE[CONVERT[UPCASE(txt[p])]], UPCASE(txt[p]) = txt[p], TRUE);
END; { DrawText }
PROCEDURE DrawTextAt(x, y : WORD; txt : STRING; alignment : BYTE);
BEGIN
CASE alignment OF
{LEFT_ALIGNED_TEXT : do nothing}
CENTERED_TEXT : DEC(x, DTextWidth(txt) DIV 2);
RIGHT_ALIGNED_TEXT : DEC(x, DTextWidth(txt));
{ELSE do nothing}
END; (* CASE *)
MoveTo(x, y);
DrawText(txt);
END; { DrawTextAt }
END. (* UNIT DRAWSTR *)